#Requires AutoHotkey v2.0
#Include tool\FileTool.ahk
#Include tool\SimLog.ahk

; 嵌套解压压缩包(v1.01)
; 有一个bug(会把同一个目录下的所有压缩包解压)(已经解决)

;;;;; ;;;;; ;;;;; ;;;;; ;;;;; ;;;;; ;;;;; ;;;;; ;;;;; ;;;;;


; 声明日志对象
logger := SimLog()
logger.logPath := EnvGet("USERPROFILE") "\faintdream\log\qianTaoJieYa.log"
logger.logPath := IniRead("qianTaoJieYa.ini", "qianTaoJieYa", "logPath", logger.logPath)

logger.log("嵌套解压: 程序开始运行...")
OnExit((er, ec) => logger.log("嵌套解压: 程序正常退出..."))

; 声明嵌套解压对象
jy := QianTaoJieYa()

; 为嵌套解压对象配置参数
jy.logger := logger ; 全局的日志对象赋值给对象jy

jy.jieYaRanJian := IniRead("qianTaoJieYa.ini", "qianTaoJieYa", "jieYaRanJian", jy.jieYaRanJian)
jy.yangemoshi := IniRead("qianTaoJieYa.ini", "qianTaoJieYa", "yangemoshi", jy.yangemoshi)

jy.withParamNoDisplay:= IniRead("qianTaoJieYa.ini", "qianTaoJieYa", "withParamNoDisplay", 1)


; 如果程序是否被传递了参数
if (A_Args.Length) > 0 { 
    logger.log("嵌套解压: 程序被传入参数,参数数量(" . A_Args.Length . ")")

    ; 遍历程序参数
    loop A_Args.Length { 

        ; 如果这个参数是一个存在的文件路径
        if(FileExist(A_Args[A_Index])){
            logger.log("嵌套解压: 尝试解压参数(" . A_Args.Length . "): " . A_Args[A_Index])
            jy.qianTaoJieYa(A_Args[A_Index])
            continue
        }
        ; 忽略其他参数
        logger.log("嵌套解压: 忽略解压参数(" . A_Args.Length . "): " . A_Args[A_Index])
    }
    if(jy.withParamNoDisplay){
        logger.log("嵌套解压: 程序正常退出(不打开窗口)...")
        ExitApp(0)
    }
} 

j := jGui()
j.display()

; 图形界面
class JGui {

    display() {

        ; 创建一个新的 GUI 窗口，窗口名为 "文件比较"
        tWindow := Gui(, "压缩包嵌套解压v1.01")

        ; 添加一个文本域，占据整个窗口，并设置默认文本
        ; w_board := tWindow.Add("Edit", "w600 h400 -Wrap Cred", "请将需要解压的文件拖入窗口(1个)...")

        centent := "程序尚在测试阶段，请在解压时做好备份`n请将需要解压的文件拖入窗口(1个)..."
        w_board := tWindow.Add("Edit", "w600 h400 -Wrap Cred", centent)

        ; 绑定拖拽事件处理函数
        tWindow.OnEvent("DropFiles", tWindow_DropFiles)

        ; 拖拽事件处理函数
        tWindow_DropFiles(GuiObj, GuiCtrlObj, FileArray, X, Y) {

            w_board.Value := "解压中，请耐心等待(可能时间有点长)..."

            for i, DroppedFile in FileArray {
                jy.qianTaoJieYa(DroppedFile)
            }

            w_board.Value := "解压完成..."
        }

        ; 显示窗口
        tWindow.Show()
    }
}


;;;;; ;;;;; ;;;;; ;;;;; ;;;;; ;;;;; ;;;;; ;;;;; ;;;;; ;;;;;

; 嵌套解压(依赖类)
class QianTaoJieYa {

    ; 简单日志工具
    logger := SimLog()

    ; 解压软件(的目录)
    jieYaRanJian := "D:\app\WinRAR\WinRAR.exe X -or"

    ; 压缩文件格式
    yaShuoWenJianGeShi := ["zip", "rar"]

    ; 解压后自动删除原始压缩包
    deleteSourceFile := false

    ; 启用严格模式(一旦程序出现任何问题,立即退出)
    yangemoshi := true

    ; 解压(文件)
    jieYa(fileName) {

        ; 压缩包所在路径(父目录)
        parentPath := this._gPBLS(fileName)

        ; 实际解压的脚本
        script := this.jieYaRanJian . " " . this._format(fileName) . " " . this._format(parentPath)

        ; 写日志
        this.logger.log("运行脚本: " . script)

        ; 运行脚本
        return RunWait(script)
    }

    ; 嵌套解压(文件)
    qianTaoJieYa(fileName) {

        this.logger.log("尝试解压(压缩包路径): " . fileName)

        ; 判断文件是否存在(如果文件不存在，直接返回)
        if (!FileExist(fileName)) {
            this.logger.log("文件不存在(压缩包路径): " . fileName)
            return "文件不存在"
        }

        ; 创建一个和压缩包同名的文件夹
        newFolder := this._fileToFolder(fileName)

        ; 将压缩包移动到这个文件夹中
        FileCopy(fileName, newFolder)

        ; 标记是否跳出循环
        flag := true

        ; 解压文件的主路径(压缩包所在目录,创建的新文件夹)
        mainPath := newFolder

        ; 遍历主路径下的所有文件(夹)，
        ; 如果有压缩包，解压它，解压之后删除解压过的压缩包
        ; 重复上面操作
        ; 直到主路径下(包括子目录)没有压缩包为止
        while (flag) {
            Loop Files, mainPath . "\*", "DFR" { ; 遍历主路径下的所有文件(夹)
                loop this.yaShuoWenJianGeShi.Length {
                    ; 判断文件是否是压缩包
                    if (this.yaShuoWenJianGeShi[A_Index] = A_LoopFileExt) {

                        ; 解压压缩包
                        code := this.jieYa(A_LoopFileFullPath)
                        sleep 10
                        if (this.yangemoshi && code != 0) {
                            MsgBox("解压出现问题,错误代码:" . code)
                            this.logger.log("解压文件 " . A_LoopFileExt " 发生错误,错误代码: " . code)
                            ExitApp(Number(code))
                        }

                        ; 解压之后删除原来的压缩包
                        FileSetAttrib("-R", A_LoopFileFullPath) ; 移除文件的只读属性
                        FileDelete(A_LoopFileFullPath)
                        ; 告诉程序主路径下可能还有压缩包
                        flag := true

                        break
                    }

                    ; 执行到之这里证明没有压缩包了，跳出循环
                    flag := false
                }
            }
            
            ; 删除压缩包
            if(this.deleteSourceFile){
                FileRecycle(fileName)
            }
        }

    }

    ; 截取最后一个反斜杠之前的字符串(Get Path Before Last Slash )
    _gPBLS(path) {

        ; 找到最后一个反斜杠的位置
        lastSlashPos := InStr(path, "\", "On", , -1)

        ; 如果没有反斜杠,将路径转换为绝对路径(继续寻找反斜杠)
        ; if (lastSlashPos == 0) {
        ;    path := A_WorkingDir "\" path
        ;    return this._gPBLS(path)
        ; }

        ; 截取路径中最后一个反斜杠之前的部分
        temp := SubStr(path, 1, lastSlashPos)

        ; 去掉 "\"
        return SubStr(temp, 1, -1)

    }

    ; 格式化文件名(给文件名加引号)
    _format(fileName) {
        if (SubStr(fileName, 1, 1) != '"') {
            return '"' . fileName . '"'
        }
        return fileName
    }

    ; 创建文件同名文件夹
    _fileToFolder(fileName) {

        ; 定义文件夹的名字
        folderName := fileName

        ; 查找文件名最后一个点的位置(文件扩展名开始的位置)
        lastDotPos := InStr(fileName, ".", 0, , -1)

        ; 截取字符串从开头到最后一个点之前的部分(文件全名去掉扩展名)
        if (lastDotPos > 0) {
            folderName := SubStr(fileName, 1, lastDotPos - 1)
        }

        indexE := 0 ; 文件夹名后缀下标
        originfolderName := folderName ; 原始文件夹名

        ; 待创建的文件夹如果已经存在，为文件名添加后缀(1)
        while (DirExist(folderName)) {
            folderName := originfolderName . "(" . ++indexE . ")"
        }

        ; 创建文件夹
        DirCreate(folderName)
        
        ; 返回文件夹的路径
        return folderName
    }

}